package com.gmcloud.common.security.service;

import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.gmcloud.common.core.constant.CacheConstants;
import com.gmcloud.common.core.constant.SecurityConstants;
import com.gmcloud.common.core.utils.RetOps;
import com.gmcloud.common.security.exception.OAuthClientException;
import com.gmcloud.upms.api.system.entity.SysOauthClientDetails;
import com.gmcloud.upms.api.system.feign.RemoteClientDetailsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.util.Arrays;
import java.util.Optional;

/**
 * @author zl.sir
 * @version 1.0
 * @since 2022/8/19 14:18
 * 查询客户端相关信息实现
 */
public class GmRemoteRegisteredClientRepository implements RegisteredClientRepository {

    private static final Logger LOGGER= LoggerFactory.getLogger(GmRemoteRegisteredClientRepository.class);

    /**
     * 刷新令牌有效期默认 30 天
     */
    private static final int refreshTokenValiditySeconds = 60 * 60 * 24 * 30;

    /**
     * 请求令牌有效期默认 12 小时
     */
    private static final int accessTokenValiditySeconds = 60 * 60 * 12;

    private final RemoteClientDetailsService clientDetailsService;

    public GmRemoteRegisteredClientRepository(RemoteClientDetailsService clientDetailsService) {
        this.clientDetailsService = clientDetailsService;
    }

    /**
     * Saves the registered client.
     *
     * <p>
     * IMPORTANT: Sensitive information should be encoded externally from the
     * implementation, e.g. {@link RegisteredClient#getClientSecret()}
     * @param registeredClient the {@link RegisteredClient}
     */

    @Override
    public void save(RegisteredClient registeredClient) {
        throw new UnsupportedOperationException();
    }

    /**
     * Returns the registered client identified by the provided {@code id}, or
     * {@code null} if not found.
     * @param id the registration identifier
     * @return the {@link RegisteredClient} if found, otherwise {@code null}
     */
    @Override
    public RegisteredClient findById(String id) {
        throw new UnsupportedOperationException();
    }

    /**
     * Returns the registered client identified by the provided {@code clientId}, or
     * {@code null} if not found.
     * @param clientId the client identifier
     * @return the {@link RegisteredClient} if found, otherwise {@code null}
     */

    /**
     * 重写原生方法支持redis缓存
     * @param clientId 客户端id
     * @return RegisteredClient
     * auth第一步，验证客户端
     */
    @Cacheable(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientId", unless = "#result == null")
    @Override
    public RegisteredClient findByClientId(String clientId) {
        SysOauthClientDetails clientDetails = RetOps
                .of(clientDetailsService.getClientDetailsById(clientId, SecurityConstants.FROM_IN)).getData()
                .orElseThrow(() -> new OAuthClientException("客户端验证异常，请检查数据库客户端配置是否正确"));

        RegisteredClient.Builder builder = RegisteredClient.withId(clientDetails.getId())
                .clientId(clientDetails.getId())
                .clientSecret(SecurityConstants.NOOP + clientDetails.getClientSecret())
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);

        // 授权模式
        Optional.ofNullable(clientDetails.getAuthorizedGrantTypes())
                .ifPresent(grants -> StringUtils.commaDelimitedListToSet(grants)
                        .forEach(s -> builder.authorizationGrantType(new AuthorizationGrantType(s))));
        // 回调地址
        Optional.ofNullable(clientDetails.getWebServerRedirectUri()).ifPresent(redirectUri -> Arrays
                .stream(redirectUri.split(StrPool.COMMA)).filter(StrUtil::isNotBlank).forEach(builder::redirectUri));

        // scope
        Optional.ofNullable(clientDetails.getScope()).ifPresent(
                scope -> Arrays.stream(scope.split(StrPool.COMMA)).filter(StrUtil::isNotBlank).forEach(builder::scope));

        return builder
                .tokenSettings(TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.REFERENCE)
                        .accessTokenTimeToLive(Duration.ofSeconds(Optional
                                .ofNullable(clientDetails.getAccessTokenValidity()).orElse(accessTokenValiditySeconds)))
                        .refreshTokenTimeToLive(
                                Duration.ofSeconds(Optional.ofNullable(clientDetails.getRefreshTokenValidity())
                                        .orElse(refreshTokenValiditySeconds)))
                        .build())
                .clientSettings(ClientSettings.builder()
                        .requireAuthorizationConsent(!BooleanUtil.toBoolean(clientDetails.getAutoapprove())).build())
                .build();
    }
}
